/*
 * To change this template, choose Tools | Templates
 * and open the template in the editor.
 */
package foxmob.mlts.component;

import com.thoughtworks.xstream.XStream;
import com.thoughtworks.xstream.io.xml.DomDriver;
import foxmob.mlts.model.QuestionPaper;
import foxmob.mlts.model.FillInTheBlanksQuestion;
import foxmob.mlts.model.MultipleChoiceQuestion;
import foxmob.mlts.model.Question;
import foxmob.mlts.model.Section;
import foxmob.mlts.model.Subsection;
import java.awt.RenderingHints.Key;
import java.io.BufferedReader;
import java.io.BufferedWriter;
import java.io.File;
import java.io.FileInputStream;
import java.io.FileNotFoundException;
import java.io.FileOutputStream;
import java.io.FileReader;
import java.io.FileWriter;
import java.io.IOException;
import java.util.ArrayList;
import java.util.Properties;
import java.util.logging.Level;
import java.util.logging.Logger;
import javax.crypto.Cipher;
import javax.crypto.spec.SecretKeySpec;
import sun.misc.BASE64Decoder;
import sun.misc.BASE64Encoder;

/**
 *
 * @author Lunayo
 *
 */
public class FileManager {
    
    private XStream xstream;
    private static FileManager fileManagerInstance;
    private Properties properties;
    
    private static final String PROPERTIES_FILE_NAME = "config.properties";
    public static final String STUDENT_ANSWER_FILE = "StudentAnswer";
    private static final String ALGORITHM = "AES";
    private static final byte[] keyValue = 
        new byte[] { 'F', '0', 'X', 'm', '0', 'B', 'M', '3', '1', 't', 'S', 'y', '5', 't', '3', 'M' };
    
    private FileManager() {
        // initialize english Test object
        initializeParser();
        properties = new Properties();
    }
    
    public static synchronized FileManager getInstanceObject() {
        if (fileManagerInstance == null) {
            fileManagerInstance = new FileManager();
        }
        return fileManagerInstance;
    }
    
    private void initializeParser() {
        // Initialize xstream API parser object
        xstream = new XStream(new DomDriver());
        // Create alias for each class name
        xstream.alias("EnglishTest",QuestionPaper.class);
        xstream.alias("FillInQuestion",FillInTheBlanksQuestion.class);
        xstream.alias("MultipleChoiceQuestion",MultipleChoiceQuestion.class);
        xstream.alias("Question", Question.class);
        xstream.alias("Section",Section.class);
        xstream.alias("Subsection",Subsection.class);
    }
    
    public QuestionPaper getTestRecord(String filename) {
        QuestionPaper testRecord = null;
        try {
            testRecord = (QuestionPaper)xstream.fromXML(readFromFile(filename));
        } catch (Exception e) {
            System.err.println("Error : " + e.getMessage());
        }
        return testRecord;
    }
    
    public boolean isRecordExists(String fileName) {
        File file = new File("record/" + fileName + ".txt");
        if (file.exists()) return true;
        return false;
    }
    
    public ArrayList<QuestionPaper> getTestRecord() {
        ArrayList<QuestionPaper> records = new ArrayList<QuestionPaper>();
        File directory = new File("record");  
        File[] files = directory.listFiles();  

        for (int i = 0; i < files.length; ++i) {  
            if (files[i].isDirectory()) continue;
            //Print out the name of files in the directory  
            // remove extension
            String fileName = 
                    files[i].getName().substring(0, 
                    files[i].getName().lastIndexOf('.'));
            if (fileName.equals("") || 
                    fileName.isEmpty()) continue;
            if (fileName.equals(STUDENT_ANSWER_FILE)) continue;
            // get the english test record and add it to array
            QuestionPaper record = getTestRecord(fileName);
            if (record == null) continue;
            records.add(record);
        }
        return records;
    }
    
    public void deleteFromFile(String filename) {
        File file = new File("record/" + filename + ".txt");
        file.delete();
    }
    
    
    public String readFromFile(String filename) throws IOException {
        String xml = null;
        String filePath = new File(".").getAbsolutePath() + "//record//";
        FileReader reader = new FileReader(filePath + filename + ".txt");
        BufferedReader in = new BufferedReader(reader);
        try {
            StringBuilder sb = new StringBuilder();
            String line = in.readLine();
            while (line != null) {
                sb.append(line);
                sb.append("\n");
                line = in.readLine();
            }
            xml = sb.toString();
            try {
                xml = decrypt(xml);
            } catch (Exception ex) {
                Logger.getLogger(FileManager.class.getName()).log(Level.SEVERE, null, ex);
            }
        } catch (Exception e) {
            System.err.println("Error : " + e.getMessage());
        } finally {
            // close the input file stream
            in.close();
        }
        
        return xml;
    }
    
    public void saveToFile(QuestionPaper record) throws IOException {
        // convert the object to xml text
        String xml = xstream.toXML(record);
        File theDir = new File("record");
        // create folder
        if (!theDir.exists()) {
            boolean result = theDir.mkdir();  
        }
        if (!StringManager.isValidName(record.getTestName())) {
            throw new IOException("not valid filename");
        }
        // create file
        String filePath = new File(".").getAbsolutePath() + "//record//";
        FileWriter writer = new FileWriter(filePath + record.getTestName() + ".txt");
        BufferedWriter out = new BufferedWriter(writer);
        // write xml text to file
        try {
            try {
                xml = encrypt(xml);
            } catch (Exception ex) {
                Logger.getLogger(FileManager.class.getName()).log(Level.SEVERE, null, ex);
            }
            out.write(xml);
            
        } catch (IOException e) {
            System.err.println("Error : " + e.getMessage());
        } finally {
            // close the file stream
            out.close();
        }
    }
    
    private static SecretKeySpec generateKey() throws Exception {
        SecretKeySpec key = new SecretKeySpec(keyValue, ALGORITHM);
        // SecretKeyFactory keyFactory = SecretKeyFactory.getInstance(ALGORITHM);
        // key = keyFactory.generateSecret(new DESKeySpec(keyValue));
        return key;
    }
    
    public static String encrypt(String valueToEnc) throws Exception {
        SecretKeySpec key = generateKey();
        Cipher c = Cipher.getInstance(ALGORITHM);
        c.init(Cipher.ENCRYPT_MODE, key);
        byte[] encValue = c.doFinal(valueToEnc.getBytes());
        String encryptedValue = new BASE64Encoder().encode(encValue);
        return encryptedValue;
    }

    public static String decrypt(String encryptedValue) throws Exception {
        SecretKeySpec key = generateKey();
        Cipher c = Cipher.getInstance(ALGORITHM);
        c.init(Cipher.DECRYPT_MODE, key);
        byte[] decordedValue = new BASE64Decoder().decodeBuffer(encryptedValue);
        byte[] decValue = c.doFinal(decordedValue);
        String decryptedValue = new String(decValue);
        return decryptedValue;
    }
    
    public void addObjectToProperties(String key, String value) throws IOException {
        properties.setProperty(key, value);
        //save properties to project root folder
        properties.store(new FileOutputStream(PROPERTIES_FILE_NAME), null);
     
    }
    
    public void removeObjectFromProperties(String key) throws IOException {
        this.properties.load(new FileInputStream(PROPERTIES_FILE_NAME));
        this.properties.remove(key);
        //save properties to project root folder
        properties.store(new FileOutputStream(PROPERTIES_FILE_NAME), null);
    }
    
    public String getObjectFromProperties(String key) throws IOException {
        this.properties.load(new FileInputStream(PROPERTIES_FILE_NAME));
        return this.properties.getProperty(key);
    }
}
